﻿using MiniFox.Caching;
using MiniFox.Components;
using MiniFox.Platform.File.Configurations;
using MiniFox.Platform.File.Models;
using MiniFox.Platform.File.Providers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MiniFox.Platform.File.Services
{
    /// <summary>
    /// 
    /// </summary>
    [AutoRegister]
    public abstract class FileSystemBase : Component
    {
        string _group;
        Dictionary<string, Exception> _errorList;
        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="group"></param>
        public FileSystemBase(string group)
        {
            _group = group;
            _errorList = new Dictionary<string, Exception>();
        }
        /// <summary>
        /// 
        /// </summary>
        [AutoWired]
        public IFileSystemConfigurationProvider ConfigProvider { get; set; }
        /// <summary>
        /// 
        /// </summary>
        public Dictionary<string, Exception> ErrorList
        {
            get { return _errorList; }
        }
        /// <summary>
        /// 
        /// </summary>
        public string Group
        {
            get { return _group; }
        }
        FileManageConfiguration _configuration;
        /// <summary>
        /// 
        /// </summary>
        protected virtual FileManageConfiguration Configuration
        {
            get
            {
                if (_configuration == null)
                {
                    _configuration = ConfigProvider.GetConfiguration();
                }
                return _configuration;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        protected virtual FileSetting FileSetting
        {
            get
            {
                return Configuration.FileSettings[Group];
            }
        }
        CacheProvider cacheProvider;
        /// <summary>
        /// 
        /// </summary>
        protected CacheProvider Cache
        {
            get
            {
                if (cacheProvider == null)
                {
                    cacheProvider = CacheService.GetCacheProvider(FileSetting.CacheName);
                }
                return cacheProvider;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        public string RouteName
        {
            get
            {
                return Configuration.RouteName;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        public bool PreDownload
        {
            get
            {
                return FileSetting.PreDownload;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileData"></param>
        /// <returns></returns>
        protected virtual string ConvertVirtualPathToPhysicalPath(FileData fileData)
        {
            var path = fileData.Path ?? string.Empty;
            string basePath = AppDomain.CurrentDomain.BaseDirectory;
            if (!string.IsNullOrWhiteSpace(FileSetting.Foler))
            {
                basePath = Path.GetFullPath(FileSetting.Foler.Trim('/'), AppDomain.CurrentDomain.BaseDirectory);
            }

            var localPath = Path.GetFullPath(path.Trim('/'), basePath);

            if (!Directory.Exists(localPath))
            {
                Directory.CreateDirectory(localPath);
            }

            var filePath = Path.GetFullPath(fileData.FileName.Trim('/'), localPath);

            return filePath;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileId"></param>
        /// <returns></returns>
        protected virtual FileData GetFileDatasFromCache(string fileId)
        {
            if (string.IsNullOrEmpty(fileId))
                return null;
            FileData fileData = Cache.Get<FileData>(fileId);
            if (fileData == null)
                return null;
            return fileData;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileData"></param>
        /// <returns></returns>
        protected virtual void SetFileDataToCache(FileData fileData)
        {
            Cache.Add(fileData.FileId, fileData, FileSetting.CacheDuration);
        }

        #region 创建压缩文件项

        /// <summary>
        /// 
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="fileData"></param>
        /// <returns></returns>
        protected virtual PackFile CreatePackFile<T>(T fileData) where T : FileData
        {
            return new PackFile
            {
                FileId = fileData.FileId,
                FileName = fileData.FileName,
                ContentType = fileData.ContentType,
                Data = fileData.Data,
                FormName = fileData.FormName,
                Group = fileData.Group,
                Path = fileData.Path,
                CreateTime = fileData.CreateTime,
                UpdateTime = fileData.UpdateTime
            };
        }

        #endregion

        #region 压缩

        private void ZipValidate(IEnumerable<FileData> fileDatas)
        {
            if (fileDatas == null || fileDatas.Count() <= 0)
                throw new InvalidOperationException("文件列表为空");
            if (fileDatas.Count() > FileSetting.MaxBatchCount)
                throw new MiniFox.Exceptions.SystemException($"一次最多只能压缩{FileSetting.MaxBatchCount}个文件");
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileName"></param>
        /// <returns></returns>
        protected virtual FileCompressor CreateFileCompressor(string fileName)
        {
            return Container.GetComponent<FileCompressor>(fileName, FileSetting.CompressLevel);
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileDatas"></param>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public ZipFileData CreateZip(IEnumerable<FileData> fileDatas, string fileName)
        {
            ZipValidate(fileDatas);
            using (FileCompressor fileCompressor = CreateFileCompressor(fileName))
            {
                Parallel.ForEach(fileDatas, (fileData) =>
                {
                    var packFile = CreatePackFile(fileData);
                    fileCompressor.AddFileToCompress(packFile);
                });

                return fileCompressor.GetResult();
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="fileDatas"></param>
        /// <param name="fileName"></param>
        /// <returns></returns>
        public async Task<ZipFileData> CreateZipAsync(IEnumerable<FileData> fileDatas, string fileName)
        {
            ZipValidate(fileDatas);
            await using (FileCompressor fileCompressor = CreateFileCompressor(fileName))
            {
                await Parallel.ForEachAsync(fileDatas, async (fileData, cancelToken) =>
                {
                    var packFile = CreatePackFile(fileData);
                    await fileCompressor.AddFileToCompressAsync(packFile);
                });

                return fileCompressor.GetResult();
            }
        }

        #endregion
    }
}
